import requests
import json
import re
import base64
from datetime import datetime, timedelta
import uuid

# ========== DeepSeek 配置 ==========
API_KEY = "sk-" 
API_URL = "https://api.deepseek.com/v1/chat/completions"
HEADERS = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {API_KEY}"
}


def encode_image_to_base64(filepath):
    try:
        with open(filepath, "rb") as f:
            return base64.b64encode(f.read()).decode("utf-8")
    except FileNotFoundError:
        print(f"[错误] 图片路径不存在: {filepath}")
        return ""

def millis_to_iso(millis):
    return datetime.utcfromtimestamp(int(millis) / 1000).isoformat() + "Z"

def extract_json_from_response(response_text):
    match = re.search(r"```json\s*(.*?)\s*```", response_text, re.DOTALL)
    json_str = match.group(1) if match else response_text
    try:
        return json.loads(json_str)
    except json.JSONDecodeError as e:
        print("无法解析 JSON：", e)
        return None

def filter_and_format_messages(raw_messages):
    filtered = []
    for msg in raw_messages:
        if msg["msg_type"] == "text":
            text = msg["message_content"]["text"]
            if not any(x in text for x in ["收到", "谢谢", "了解了"]):
                filtered.append({
                    "message_id": msg["message_id"],
                    "sender_id": msg["sender_id"],
                    "msg_type": "text",
                    "message_content": {
                        "text": text
                    },
                    "create_time": millis_to_iso(msg.get("create_time", ""))
                })
        elif msg["msg_type"] == "image":
            filtered.append({
                "message_id": msg["message_id"],
                "sender_id": msg["sender_id"],
                "msg_type": "image",
                "message_content": {
                    "image_key": msg["message_content"]["image_key"],
                    "base64": msg.get("base64", "")
                },
                "create_time": millis_to_iso(msg.get("create_time", ""))
            })
    return filtered


def cluster_and_classify(messages):
    prompt_lines = [
        f"- [{msg['create_time']}] [TEXT] {msg['message_content']['text']} (message_id={msg['message_id']}, sender_id={msg['sender_id']})"
        for msg in messages if msg["msg_type"] == "text"
    ]

    prompt_text = "\n".join(prompt_lines)
    prompt = f"""
<context>
你是一个建筑工地巡检助手。在巡检过程中，你需要将信息聚类、分类，并基于每个聚类生成专业的问题描述，并提取关键信息。
</context>

<task_rules>

对于以下巡检留言列表，请执行以下任务：

1. 将留言按照内容相关性进行聚类，每个聚类形成一个事件卡；
2. 对每个事件卡进行分类，类别为以下五类之一：
    - 巡检问题（现场隐患、风险点）
    - 验收
    - 旁站
    - 闭环
    - 其他
3. 对于被分类为“问题记录”的事件卡，还需要：
    - 生成一段专业、规范的“问题描述”（要求简洁清晰）
    - 尝试提取：
        - 施工部位 (Location)
        - 责任单位 (Responsible Party)
    - 如果施工部位或责任单位缺失，请在提取内容中标记为 "[部位信息缺失]" 或 "[责任单位信息缺失]"
4. 请在聚类的每条留言中，**保留以下字段不变**：
    - message_id: 留言的唯一标识
    - sender_id: 发送者飞书用户ID
    - msg_type: 保持原样
    - message_content: 保持原样
    - create_time: 保持原样
    注意：**不要更改任何字段名或字段值。**

</task_rules>

<output_rules>
请返回一个 JSON 数组，数组里的每个元素都是一个事件卡（object），结构如下：

[
  {{
    "event_id": 1,
    "summary": "发生在3号楼东南角的脚手架隐患",
    "messages": [
      {{
        "message_id": "om_3",
        "sender_id": "user1",
        "msg_type": "text",
        "message_content": {{"text":"在3号楼东南角发现脚手架松动，有坠落风险"}},
        "create_time": "2024-03-31T22:30:00Z"
      }},
      {{
        "message_id": "om_5",
        "sender_id": "user1",
        "msg_type": "text",
        "message_content": {{"text":"责任人张三已通知整改"}},
        "create_time": "2024-03-31T22:31:10Z"
      }}
    ],
    "class": "巡检问题",
    "issue_description": "3号楼东南角脚手架松动，存在高空坠落风险，需立即整改。",
    "location": "3号楼东南角",
    "responsible_party": "张三"
  }},
  ...
]
</output_rules>


以下是巡检留言信息：
{prompt_text}
"""
    
    payload = {
        "model": "deepseek-chat",
        "messages": [
            {"role": "system", "content": "你是一个建筑巡检信息聚类、分类与关键信息提取助手。"},
            {"role": "user", "content": prompt}
        ],
        "temperature": 0.3
    }

    try:
        response = requests.post(API_URL, headers=HEADERS, json=payload)
        response.raise_for_status()
        return extract_json_from_response(response.json()["choices"][0]["message"]["content"])
    except requests.exceptions.RequestException as e:
        print("请求失败:", e)
        return None

def assign_images_to_events(events, images, time_window_minutes=10):
    for event in events:
        event_time_range = [
            datetime.fromisoformat(msg["create_time"].replace("Z", "+00:00"))
            for msg in event["messages"]
        ]
        min_time = min(event_time_range)
        max_time = max(event_time_range)
        window_start = min_time - timedelta(minutes=time_window_minutes)
        window_end = max_time + timedelta(minutes=time_window_minutes)
        #print(window_start, window_end)

        candidate_imgs = []
        for img in images:
            try:
                img_time = datetime.fromisoformat(img["create_time"].replace("Z", "+00:00"))
                if window_start <= img_time <= window_end:
                    candidate_imgs.append({
                        "base64": img["message_content"]["base64"],
                        "filename": img["message_content"]["image_key"],
                        "create_time": img_time
                    })
            except:
                continue

        event["candidate_images"] = candidate_imgs
    return events

import uuid

def convert_events_to_issue_cards(events, project_id):
    issue_cards = []
    now = datetime.utcnow().isoformat() + "Z"

    for event in events:
        original_message_ids = []
        reporter_user_id = None
        raw_texts = []

        for msg in event.get("messages", []):
            if msg.get("msg_type") == "text":
                original_message_ids.append(msg.get("message_id", "unknown"))
                raw_texts.append(msg["message_content"].get("text", ""))
                if not reporter_user_id:
                    reporter_user_id = msg.get("sender_id", "unknown")

        card = {
            "id": str(uuid.uuid4()),
            "original_message_ids": original_message_ids,
            "reporter_user_id": reporter_user_id or "unknown",
            "reporter_name": "unknown",
            "record_timestamp": now,
            "raw_text_input": "\n".join(raw_texts),
            "image_urls": [img["filename"] for img in event.get("candidate_images", [])],
            "ai_generated_description": event.get("issue_description", ""),
            "extracted_location": event.get("location", "[部位信息缺失]"),
            "extracted_responsible_party": event.get("responsible_party", "[责任单位信息缺失]"),
            "status": "待处理",
            "last_updated_timestamp": now,
            "project_id": project_id,
            "is_deleted": False,
            "generated_document_ids": [],
            "is_merged_card": False,
            "merged_from_card_ids": [],
            "merged_into_card_id": ""
        }
        issue_cards.append(card)

    return issue_cards

def print_issue_cards(issue_cards):
    for idx, card in enumerate(issue_cards, 1):
        print(f"\n 卡片 {idx}:")
        print(f"  - id: {card['id']}")
        print(f"  - project_id: {card['project_id']}")
        print(f"  - status: {card['status']}")
        print(f"  - reporter_user_id: {card['reporter_user_id']}")
        print(f"  - reporter_name: {card['reporter_name']}")
        print(f"  - record_timestamp: {card['record_timestamp']}")
        print(f"  - last_updated_timestamp: {card['last_updated_timestamp']}")
        print(f"  - original_message_ids: {card['original_message_ids']}")
        print(f"  - raw_text_input:\n      {card['raw_text_input']}")
        print(f"  - ai_generated_description:\n      {card['ai_generated_description']}")
        print(f"  - extracted_location: {card['extracted_location']}")
        print(f"  - extracted_responsible_party: {card['extracted_responsible_party']}")
        print(f"  - image_urls: {card['image_urls']}")
        print(f"  - is_merged_card: {card['is_merged_card']}")
        print(f"  - merged_from_card_ids: {card['merged_from_card_ids']}")
        print(f"  - merged_into_card_id: {card['merged_into_card_id']}")
        print(f"  - generated_document_ids: {card['generated_document_ids']}")


def main():
    img2 = encode_image_to_base64("IMG_2.jpg")
    img3 = encode_image_to_base64("image03.jpg")

    raw_messages = [
        {
            "message_id": "om_3",
            "msg_type": "text",
            "create_time": "1711933800000",  # 2024-03-31 22:30:00 UTC
            "sender_id": "user1",
            "message_content": {"text": "在3号楼东南角发现脚手架松动，有坠落风险"}
        },
        {
            "message_id": "om_5",
            "msg_type": "text",
            "create_time": "1711933870000",  # 2024-03-31 22:31:10 UTC
            "sender_id": "user1",
            "message_content": {"text": "责任人张三已通知整改"}
        },
        {
            "message_id": "om_6",
            "msg_type": "text",
            "create_time": "1711934100000",  # 2024-03-31 22:35:00 UTC
            "sender_id": "user1",
            "message_content": {"text": "收到，谢谢"}
        },
        {
            "message_id": "om_7",
            "msg_type": "text",
            "create_time": "1711940400000",  # 2024-03-31 23:00:00 UTC
            "sender_id": "user1",
            "message_content": {"text": "2号楼发现电缆裸露，存在触电危险"}
        },
        {
            "message_id": "om_8",
            "msg_type": "image",
            "create_time": "1711940460000",  # 2024-03-31 23:01:00 UTC
            "sender_id": "user2",
            "message_content": {"image_key": "IMG_2.jpg"},
            "base64": img2
        },
        {
            "message_id": "om_9",
            "msg_type": "text",
            "create_time": "1711940700000",  # 2024-03-31 23:05:00 UTC
            "sender_id": "user2",
            "message_content": {"text": "监理回复：了解了"}
        },
        {
            "message_id": "om_extra_07",
            "msg_type": "text",
            "create_time": "1711952400000",  # 2024-04-01 02:00:00 UTC
            "sender_id": "user5",
            "message_content": {"text": "今日上午9点，3号楼主体钢筋绑扎作业开始"}
        },
        {
            "message_id": "om_extra_08",
            "msg_type": "text",
            "create_time": "1711953300000",  # 2024-04-01 02:15:00 UTC
            "sender_id": "user5",
            "message_content": {"text": "监理张工已到场确认施工规范"}
        },
        {
            "message_id": "om_extra_05",
            "msg_type": "text",
            "create_time": "1712028300000",  # 2024-04-02 08:45:00 UTC
            "sender_id": "user4",
            "message_content": {"text": "5号楼地下室防水层施工于昨日完成，今日进行初步验收"}
        },
        {
            "message_id": "om_extra_06",
            "msg_type": "text",
            "create_time": "1712028600000",  # 2024-04-02 08:50:00 UTC
            "sender_id": "user4",
            "message_content": {"text": "验收人员已到场，准备进行验收"}
        },
        {
            "message_id": "om_a",
            "msg_type": "image",
            "create_time": "1712029900000",  
            "sender_id": "user4",
            "message_content": {"image_key": "image03.jpg"},
            "base64": img3
        }
    ]

    filtered = filter_and_format_messages(raw_messages)
    text_msgs = [m for m in filtered if m["msg_type"] == "text"]
    image_msgs = [m for m in filtered if m["msg_type"] == "image"]

    print("\n所有图像消息:")
    for im in image_msgs:
        print(f"- {im['message_content']['image_key']} @ {im['create_time']}")



    events = cluster_and_classify(text_msgs)
    if events is None:
        print("聚类和分类失败")
        return

    events = assign_images_to_events(events, image_msgs)
    issue_cards = convert_events_to_issue_cards(events, project_id="TestProject001")
    
    print_issue_cards(issue_cards)



if __name__ == "__main__":
    main()
